package org.particles.domain.sim;

import java.util.Arrays;
import java.util.List;

import org.particles.common.CommonUtil;
import org.particles.domain.ExpFunction;
import org.particles.domain.field.AbstractField;
import org.particles.domain.world.CompositeOutOfBoundResolver;
import org.particles.domain.world.CoolingOutOfBoundResolver;
import org.particles.domain.world.FieldRepository;
import org.particles.domain.world.OutOfWorldBoundResolver;
import org.particles.domain.world.RicochetOutOfBoundResolver;
import org.particles.domain.world.World;
import org.particles.domain.world.World2D;

public class GravitySimulation extends ElectronSimulation {
	public static final int PARAM_GFIELD_STRENGTH = CommonUtil.uniqueIndex();
	public static final int PARAM_COOLING = CommonUtil.uniqueIndex();
	protected AbstractField gravityField;
	private CoolingOutOfBoundResolver oor = new CoolingOutOfBoundResolver(1.05, false);
	
	public GravitySimulation() {
		super();
		setName("Gravity");
		setDistToZero(23);
		setFieldStrength(10);
		setNumberParticles(200);
		setFieldMarkers(getFieldMarkers() | FieldRepository.ATTRACT_EXP);
		setMaxParticleImpulse(1);
		gravityField = (AbstractField) 
				getFieldRepository().getField(FieldRepository.ATTRACT_EXP);
		final double gravityFieldStrength = 10;
		gravityField.setDistanceFunc(new ExpFunction(gravityFieldStrength));
		gravityField.setStrength(gravityFieldStrength);
	}
	
	@Override
	public World createWorld() {
		final OutOfWorldBoundResolver resolver = new CompositeOutOfBoundResolver(
				Arrays.asList(new OutOfWorldBoundResolver[] {
						oor,
						new RicochetOutOfBoundResolver(),
//						new DisposeOutOfBoundResolver(
//								-900, getWorldWidth() + 900, -900, getWorldHeight() + 900),
				}));
		final World2D world = (World2D) super.createWorld();
		world.setOutOfBoundResolver(resolver);
		return world;
	}

	@Override
	public boolean changeParam(int paramCode, double newValue) {
		if(super.changeParam(paramCode, newValue)) {
			return true;
		}
		else {
			if(PARAM_GFIELD_STRENGTH == paramCode) {
				setGravityFieldStrength(newValue); 
				return true;
			}
			if(PARAM_COOLING == paramCode) {
				oor.setSlowFactor(newValue);
				return true;
			}
			else {
				return false;
			}
		}
	}

	@Override
	public List<SimParam> availableParams() {
		final List<SimParam> params = super.availableParams();
		SimParam param;
		param = new SimParam(PARAM_GFIELD_STRENGTH, "gravity field strength", getGravityFieldStrength());
		param.setMinValue(0D);
		param.setMaxValue(1000D);
		params.add(param);
		param = new SimParam(PARAM_COOLING, "particle slowing factor", oor.getSlowFactor());
		param.setMinValue(0D);
		param.setMaxValue(10D);
		params.add(param);
		return params;
	}

	public double getGravityFieldStrength() {
		return this.gravityField.getStrength();
	}

	public void setGravityFieldStrength(final double gravityFieldStrength) {
		this.gravityField.setStrength(gravityFieldStrength);
	}
}
